package cn.skyclass.utils
{
	/**
	 * MD5 Message-Digest (RFC1321)
	 */
	public class Md5Digest
	{
		public static function md5(input:String):String{
			if(input){
				var dig:Md5Digest = new Md5Digest();
				return dig.MD5(input);
			}else{
				return "";
			}
		}
		
		public static function md5Ex(input:String):String{
			var encrypted:String = md5(input);
			return md5(encrypted + encrypted);
		}
		
		
		private var MD5_T:Array = new Array(
			0x00000000, 0xd76aa478, 0xe8c7b756, 0x242070db,
			0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613,
			0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1,
			0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e,
			0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51,
			0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681,
			0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87,
			0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9,
			0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122,
			0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60,
			0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085,
			0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8,
			0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7,
			0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d,
			0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314,
			0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb,
			0xeb86d391
		);
		
		private var MD5_round1:Array = new Array(
			new Array(0, 7, 1), new Array(1, 12, 2), new Array(2, 17, 3), new Array(3, 22, 4),
			new Array(4, 7, 5), new Array(5, 12, 6), new Array(6, 17, 7), new Array(7, 22, 8),
			new Array(8, 7, 9), new Array(9, 12, 10), new Array(10, 17, 11), new Array(11, 22, 12),
			new Array(12, 7, 13), new Array(13, 12, 14), new Array(14, 17, 15), new Array(15, 22, 16)
		);
		
		private var MD5_round2:Array = new Array(
			new Array(1, 5, 17), new Array(6, 9, 18), new Array(11, 14, 19), new Array(0, 20, 20),
			new Array(5, 5, 21), new Array(10, 9, 22), new Array(15, 14, 23), new Array(4, 20, 24),
			new Array(9, 5, 25), new Array(14, 9, 26), new Array(3, 14, 27), new Array(8, 20, 28),
			new Array(13, 5, 29), new Array(2, 9, 30), new Array(7, 14, 31), new Array(12, 20, 32)
		);
		
		private var MD5_round3:Array = new Array(
			new Array(5, 4, 33), new Array(8, 11, 34), new Array(11, 16, 35), new Array(14, 23, 36),
			new Array(1, 4, 37), new Array(4, 11, 38), new Array(7, 16, 39), new Array(10, 23, 40),
			new Array(13, 4, 41), new Array(0, 11, 42), new Array(3, 16, 43), new Array(6, 23, 44),
			new Array(9, 4, 45), new Array(12, 11, 46), new Array(15, 16, 47), new Array(2, 23, 48)
		);
		
		private var MD5_round4:Array = new Array(
			new Array(0, 6, 49), new Array(7, 10, 50), new Array(14, 15, 51), new Array(5, 21, 52),
			new Array(12, 6, 53), new Array(3, 10, 54), new Array(10, 15, 55), new Array(1, 21, 56),
			new Array(8, 6, 57), new Array(15, 10, 58), new Array(6, 15, 59), new Array(13, 21, 60),
			new Array(4, 6, 61), new Array(11, 10, 62), new Array(2, 15, 63), new Array(9, 21, 64)
		);
		
		private var MD5_round:Array = new Array(
			new Array(MD5_F, MD5_round1),
			new Array(MD5_G, MD5_round2),
			new Array(MD5_H, MD5_round3),
			new Array(MD5_I, MD5_round4)
		);
		
		private function MD5_F(x:*, y:*, z:*):* {
			return (x & y) | (~x & z);
		}
		
		private function MD5_G(x:*, y:*, z:*):* {
			return (x & z) | (y & ~z);
		}
		
		private function MD5_H(x:*, y:*, z:*):* {
			return x ^ y ^ z;
		}
		
		private function MD5_I(x:*, y:*, z:*):* {
			return y ^ (x | ~z);
		}
		
		private function MD5_pack(n32:*):Array {
			return new Array(n32 & 0xff, (n32 >>> 8) & 0xff, (n32 >>> 16) & 0xff, (n32 >>> 24) & 0xff);
		}
		
		private function MD5_hash(seed:String):Array {
			var f:Function, r:Array, tmp:*;
			var i:*, j:*, k:*;
			var state:Array = new Array(0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476);
			var abcd:Array = new Array(0, 1, 2, 3);
			var x:Array = new Array(16);
			var s:Array = new Array(4);
			var dataArray:Array = new Array();
			var currentChar:* = 0;
		
			//
			// UCS2 or 4 -> UTF-8
			// http://www.unicode.org/versions/corrigendum1.html
			//
			for (i = 0; i < seed.length; i++) {
				j = seed.charCodeAt(i);
				if ( j <= 0x7F ) {
					dataArray[currentChar++] = j;
				} else if ( j <= 0x7FF ) {
					dataArray[currentChar++] = ((j >> 6)  & 0x3F) | 0xC0;
					dataArray[currentChar++] = ( j        & 0x3F) | 0x80;
				} else if ( j <= 0xFFFF ) {
					dataArray[currentChar++] = ((j >> 12) & 0x0F) | 0xE0;
					dataArray[currentChar++] = ((j >> 6)  & 0x3F) | 0x80;
					dataArray[currentChar++] = ( j        & 0x3F) | 0x80;
				} else {
					dataArray[currentChar++] = ((j >> 18) & 0x07) | 0xF0;
					dataArray[currentChar++] = ((j >> 12) & 0x3F) | 0x80;
					dataArray[currentChar++] = ((j >> 6)  & 0x3F) | 0x80;
					dataArray[currentChar++] = ( j        & 0x3F) | 0x80;
				}
			}
		
			var len:* = dataArray.length;
			var index:* = len & 0x3f;
			var padLen:* = ( index < 56 )? 56 - index : 120 - index;
		
			if ( padLen > 0 ) {
				dataArray[currentChar++] = 128;
				for (i = 0; i < padLen-1; i++, currentChar++) {
					dataArray[currentChar] = 0;
				}
			}
			dataArray = dataArray.concat(MD5_pack(len * 8), MD5_pack(0));
		
			len += padLen + 8;
			for ( k = 0; k < len; k += 64 ) {
				for ( i = 0, j = k; i < 16; i++, j += 4 ) {
					x[i] = dataArray[j] | (dataArray[j+1] << 8) | (dataArray[j+2] << 16) | (dataArray[j+3] << 24);
				}
				for ( i = 0; i < 4; i++ ) {
					s[i] = state[i];
				}
				for ( i = 0; i < 4; i++ ) {
					f = MD5_round[i][0];
					r = MD5_round[i][1];
					for ( j = 0; j < 16; j++ ) {
						MD5_apply_round(x, s, f, abcd, r[j]);
						tmp = abcd[0];
						abcd[0] = abcd[3];
						abcd[3] = abcd[2];
						abcd[2] = abcd[1];
						abcd[1] = tmp;
					}
				}
				for ( i = 0; i < 4; i++ ) {
					state[i] += s[i];
					state[i] = MD5_number(state[i]);
				}
			}
			dataArray = new Array();
			return dataArray.concat(MD5_pack(state[0]), MD5_pack(state[1]), MD5_pack(state[2]), MD5_pack(state[3]));
		}
		
		private function MD5_number(n:*):* {
			while ( n < 0 ) {
				n += 4294967296;
			}
			while ( n > 4294967295 ) {
				n -= 4294967296;
			}
			return n;
		}
		
		private function MD5_apply_round(x:*, s:*, f:Function, abcd:*, r:*):void {
			var a:*, b:*, c:*, d:*;
			var kk:*, ss:*, ii:*;
			var t:*, u:*;
			a = abcd[0];
			b = abcd[1];
			c = abcd[2];
			d = abcd[3];
			kk = r[0];
			ss = r[1];
			ii = r[2];
			u = f(s[b], s[c], s[d]);
			t = s[a] + u + x[kk] + MD5_T[ii];
			t = MD5_number(t);
			t = (t << ss) | (t >>> (32 - ss));
			t += s[b];
			s[a] = MD5_number(t);
		}
		
		private function MD5(seed:String):String {
			var i:*, out:String, c:*;
			var dataArray:Array = new Array();
			dataArray = MD5_hash(seed);
			out = "";
			for ( i = 0; i < 16; i++ ) {
				c = dataArray[i];
				out += "0123456789abcdef".charAt((c >> 4) & 0xf);
				out += "0123456789abcdef".charAt(c & 0xf);
			}
			return out;
		}
		
		//
		// unused
		//
		
		private function MD5_or(a:*, b:*):* {
			var lsb:* = (a & 0x1) | (b & 0x1);
			var msb31:* = (a >>> 1) | (b >>> 1);
			return (msb31 << 1) | lsb;
		}
		
		private function MD5_xor(a:*, b:*):* {
			var lsb:* = (a & 0x1) ^ (b & 0x1);
			var msb31:* = (a >>> 1) ^ (b >>> 1);
			return (msb31 << 1) | lsb;
		}
		
		private function MD5_and(a:*, b:*):* {
		  var lsb:* = (a & 0x1) & (b & 0x1);
		  var msb31:* = (a >>> 1) & (b >>> 1);
		  return (msb31 << 1) | lsb;
		}
		
		private function MD5_add(x:*, y:*):* {
			var lsw:* = (x & 0xFFFF)+(y & 0xFFFF);
			var msw:* = (x >> 16)+(y >> 16)+(lsw >> 16);
			return (msw << 16) | (lsw & 0xFFFF);
		}
		
		
	}
}